package com.dbms;

import com.dbms.storage.BufferPoolManager;
import com.dbms.storage.DiskManager;
import com.dbms.storage.Page;
import com.dbms.transaction.LogManager;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * 【独立演示程序】BufferPoolManager 功能测试类
 *
 * 功能目标：
 * 1. 演示磁盘页面的创建与写入。
 * 2. 演示缓冲池（Buffer Pool）的页面加载过程。
 * 3. 验证 LRU 替换策略：当缓冲池满时，最久未使用的页面会被驱逐。
 * 4. 展示页面命中率（HIT / MISS）的统计信息。
 *
 * 演示逻辑：
 * - 步骤1：初始化数据库文件和缓冲池环境。
 * - 步骤2：在磁盘创建并写入多个页面。
 * - 步骤3：顺序访问页面，填满缓冲池。
 * - 步骤4：访问新页面，触发 LRU 替换。
 * - 步骤5：重新访问被驱逐的页面，验证磁盘重新加载。
 * - 步骤6：访问已在缓冲池中的页面，验证命中并更新 LRU 队列。
 */
public class BufferPoolTest {

    public static void main(String[] args) throws IOException {
        String dbFileName = "buffer_pool_test.db";
        String logFileName = "buffer_pool_test.log";
        int bufferPoolSize = 4; // 设置缓冲池容量（小容量便于观察替换过程）

        // =========================================================
        // 1. 初始化环境
        // =========================================================
        System.out.println("--- 1. 初始化环境 ---");
        // 删除旧文件，确保每次运行都是全新环境
        new File(dbFileName).delete();
        new File(logFileName).delete();

        // 创建磁盘管理器、日志管理器和缓冲池管理器
        DiskManager diskManager = new DiskManager(dbFileName);
        LogManager logManager = new LogManager(logFileName);
        BufferPoolManager bpm = new BufferPoolManager(bufferPoolSize, diskManager, logManager);

        System.out.println("成功初始化！数据库文件: " + dbFileName + ", 缓冲池大小: " + bufferPoolSize);
        System.out.println("=========================================================\n");


        // =========================================================
        // 2. 在磁盘上创建页面并写入数据
        // =========================================================
        System.out.println("--- 2. 在磁盘上创建 6 个页面并写入初始数据 ---");
        for (int i = 0; i < 6; i++) {
            // 分配新页面 ID
            int pageId = diskManager.allocatePage();
            byte[] data = new byte[DiskManager.PAGE_SIZE];
            // 向页面写入字符串数据
            String message = "这是磁盘上页面 " + pageId + " 的内容。";
            System.arraycopy(message.getBytes(StandardCharsets.UTF_8), 0, data, 0, message.getBytes().length);
            // 写入磁盘
            diskManager.writePage(pageId, data);
        }
        System.out.println("数据写入完毕，磁盘上现在有 " + diskManager.getNumPages() + " 个页面。");
        System.out.println("=========================================================\n");


        // =========================================================
        // 3. 顺序访问页面，填充缓冲池
        // =========================================================
        System.out.println("--- 3. 顺序访问页面 0, 1, 2, 3，填满缓冲池 ---");
        for (int i = 0; i < bufferPoolSize; i++) {
            System.out.println("--> 操作: 获取页面 " + i);
            Page p = bpm.fetchPage(i); // 从缓冲池或磁盘中获取页面
            System.out.println("    获取成功！页面 " + p.getPageId() + " 已载入内存。");
            bpm.unpinPage(i, false);   // 模拟使用完释放（unpin）
            System.out.println("    当前LRU队列 (最左边=最先被替换): " + bpm.getLruList());
            System.out.println("    " + bpm.getStats()); // 显示缓冲池统计（命中率等）
            System.out.println();
        }
        System.out.println("缓冲池已满。");
        System.out.println("=========================================================\n");


        // =========================================================
        // 4. 触发LRU替换
        // =========================================================
        System.out.println("--- 4. 访问一个新页面 (Page 4)，触发LRU替换 ---");
        System.out.println("--> 操作: 获取页面 4");
        System.out.println("    预期: 缓冲池已满，最久未使用的页面 0 将被驱逐。");
        Page p4 = bpm.fetchPage(4);
        System.out.println("    获取成功！页面 " + p4.getPageId() + " 已载入内存。");
        bpm.unpinPage(4, false);
        System.out.println("    当前LRU队列 (最左边=最先被替换): " + bpm.getLruList());
        System.out.println("    " + bpm.getStats());
        System.out.println("=========================================================\n");


        // =========================================================
        // 5. 再次访问被替换出去的页面
        // =========================================================
        System.out.println("--- 5. 再次访问被替换出去的页面 (Page 0) ---");
        System.out.println("--> 操作: 获取页面 0");
        System.out.println("    预期: 页面 0 不在缓冲池中 (MISS)，需要从磁盘加载。最久未使用的页面 1 将被驱逐。");
        Page p0 = bpm.fetchPage(0);
        System.out.println("    获取成功！页面 " + p0.getPageId() + " 已载入内存。");
        bpm.unpinPage(0, false);
        System.out.println("    当前LRU队列 (最左边=最先被替换): " + bpm.getLruList());
        System.out.println("    " + bpm.getStats());
        System.out.println("=========================================================\n");


        // =========================================================
        // 6. 访问缓冲池中的页面（验证命中与LRU更新）
        // =========================================================
        System.out.println("--- 6. 访问一个已在缓冲池中的页面 (Page 2)，验证命中和LRU更新 ---");
        System.out.println("--> 操作: 获取页面 2");
        System.out.println("    预期: 页面 2 在缓冲池中 (HIT)，并且被移动到LRU队列的末尾（最近使用）。");
        Page p2 = bpm.fetchPage(2);
        System.out.println("    获取成功！页面 " + p2.getPageId() + " 已载入内存。");
        bpm.unpinPage(2, false);
        System.out.println("    当前LRU队列 (最左边=最先被替换): " + bpm.getLruList());
        System.out.println("    " + bpm.getStats());
        System.out.println("=========================================================\n");

        System.out.println("演示结束。");
    }
}
